/*
 * TabHost.cpp
 *
 *  Created on: 2012-10-9
 *      Author: yangentao@gmail.com
 */

#include "TabHost.h"
#include "Graphic.h"
#include "UiUtil.h"
#include  "UiConfig.h"

//TODO when tab size > client width
//TODO GradientFill
//TODO when client size < TABBAR_HEIGHT

inline COLORREF tabBodySelectColor() {
	return colour(coTabBodySelect);
}

inline COLORREF tabFocusLightColor() {
	return colour(coTabFocusLight);
}

inline COLORREF tabFocusColor() {
	return colour(coTabFocus);
}

inline COLORREF tabBodyColor() {
	return colour(coTabBody);
}
inline COLORREF tabBorderColor() {
	return colour(coTabBorder);
}

inline int tabBarHeight() {
	return dic(dicTabBarHeight);
}

inline int diameterWidth() {
	return dic(dicTabRadiusWidth) * 2;
}
inline int diameterHeight() {
	return dic(dicTabRadiusHeight) * 2;
}
inline int radiusWidth() {
	return dic(dicTabRadiusWidth);
}
inline int radiusHeight() {
	return dic(dicTabRadiusHeight);
}
inline int pageMargin() {
	return dic(dicTabPageMargin);
}

TabPage::TabPage() {
	image = 0;
	x = 0;
	y = 0;
	width = 0;
	height = 0;
	textHeight = 0;
	textWidth = 0;
	host = 0;
}
TabPage::~TabPage() {

}
Rect TabPage::getRect() {
	return Rect(x, y, x + width, y + height);
}

int TabPage::calTabWidth(Canvas& g, int x_, int y_, int height_) {
	x = x_;
	y = y_;
	height = height_;

	TEXTMETRIC mt = { 0 };
	g.textMetric(&mt);
	textHeight = mt.tmHeight;

	int w = g.textWidth(text);
	textWidth = clip(dic(dicTabTextMinWidth), dic(dicTabTextMaxWidth), w);

	width = dic(dicTabIconWidth) + dic(dicTabTextMargin) + textWidth + dic(dicTabTextMargin) + dic(dicTabCloseBmpWidth);
	return width;
}
Rect TabPage::getIconRect() {
	Rect r;
	r.left = 0;
	r.top = (height - dic(dicTabIconHeight)) / 2;
	r.right = r.left + dic(dicTabIconWidth);
	r.bottom = r.top + dic(dicTabIconHeight);
	r.offset(x, y);
	return r;
}
Rect TabPage::getTextRect() {
	Rect r;
	r.left = dic(dicTabIconWidth) + dic(dicTabTextMargin);
	r.top = (height - textHeight) / 2;
	r.right = r.left + textWidth;
//	r.bottom = r.top + textHeight;
	r.bottom = getIconRect().bottom;
	r.offset(x, y);
	return r;
}
Rect TabPage::getCloseRect() {
	Rect r;
	r.left = width - dic(dicTabCloseBmpWidth);
	r.right = r.left + dic(dicTabCloseBmpWidth);
	r.top = (height - dic(dicTabCloseBmpHeight)) / 2;
	r.bottom = r.top + dic(dicTabCloseBmpHeight);
	r.offset(x, y);
	return r;
}
void TabPage::close() {
	page->closeWindow();
}
void TabPage::drawIcon(Canvas& g) {
	//draw icon
	Rect iconRect = getIconRect();
	g.fillRectangle(iconRect, RGB(255,0,0) );
}
void TabPage::drawText(Canvas& g) {
	Rect r = getTextRect();
	g.drawTextTransparent(r, r.bottom, text);
}
void TabPage::drawClose(Canvas& g) {
	Rect r = getCloseRect();
	Point pt = host->getCursorPos();
	bool focus = r.Contains(pt.x, pt.y);
	HBITMAP bmp = getCloseBitmap(focus);
	g.transparentBlt(Point(r.left, r.top), bmp, colour(coTabCloseMask));
}
void TabPage::drawTab(Canvas& g) {
	drawIcon(g);
	drawText(g);
	drawClose(g);
}

TabHost::TabHost() {
	brSelect = 0;
	brFocus = 0;
	bmSelect = 0;
	bmFocus = 0;

	Super::addMessageListener(&adapter);
	adapter.OnPaint = boost::bind(&TabHost::onPaint, this, _1, _2, _3);
	adapter.OnClick = boost::bind(&TabHost::onClick, this, _1);
	adapter.OnMouseMove = boost::bind(&TabHost::onMouseMove, this, _1);
	adapter.OnMouseHover = boost::bind(&TabHost::onMouseHover, this, _1);
	adapter.OnMouseLeave = boost::bind(&TabHost::onMouseLeave, this, _1);
	adapter.OnMouseEnter = boost::bind(&TabHost::onMouseEnter, this, _1);
	adapter.OnKillFocus = boost::bind(&TabHost::onFocusChange, this, _1, false, true);
	adapter.OnChildKillFocus = boost::bind(&TabHost::onFocusChange, this, _1, true, true);
	adapter.OnSetFocus = boost::bind(&TabHost::onFocusChange, this, _1, false, false);
	adapter.OnChildSetFocus = boost::bind(&TabHost::onFocusChange, this, _1, true, false);
	Super::setAutoFocus(true);
	Super::setAutoTrackHover(true, 50);
	initBrushes();

	pages.setTag(-1);
}

TabHost::~TabHost() {
	cleanBrushes();
}
void fillColorSelect(COLORREF bits[]) {
	int n = 1;
	for (int i = tabBarHeight() - 1; i >= 0; --i) {
		bits[i] = RGB(220 -n, 220 -n, 220 -n);
		n += 3;
	}
}
void fillColorFocus(COLORREF bits[]) {
	bits[tabBarHeight() - 1] = reverseColorRB(tabFocusColor());
	bits[0] = reverseColorRB(tabFocusLightColor());
	transformColor(bits, tabBarHeight());
}
void TabHost::initBrushes() {
	int height = tabBarHeight();
	COLORREF defaultColor = _colors[coTabCloseMask];
	{
		Array<COLORREF> bitArr(defaultColor, height);
		fillColorSelect(bitArr.buffer);
		bmSelect = CreateBitmap(1, height, 1, 32, bitArr.buffer);
		brSelect = CreatePatternBrush(bmSelect);
	}
	{
		Array<COLORREF> bitArr(defaultColor, height);
		fillColorFocus(bitArr.buffer);
		bmFocus = CreateBitmap(1, height, 1, 32, bitArr.buffer);
		brFocus = CreatePatternBrush(bmFocus);
	}
}
void TabHost::cleanBrushes() {
	if (brSelect) {
		DeleteObject(brSelect);
	}
	if (bmSelect) {
		DeleteObject(bmSelect);
	}
	if (brFocus) {
		DeleteObject(brFocus);
	}
	if (bmFocus) {
		DeleteObject(bmFocus);
	}
}

HBRUSH TabHost::getSelectBrush() {
	return brSelect;
}

HBRUSH TabHost::getFocusBrush() {
	return brFocus;
}

void TabHost::onFocusChange(HWND hWnd, bool child, bool isKill) {
	Super::invalidate();
}

HWND TabHost::currentPage() {
	TabPage* tab = getSelectTab();
	if (tab) {
		return tab->page->getWnd();
	}
	return 0;
}

void TabHost::removePage(HWND tab) {
	for (int i = 0; i < pages.size(); ++i) {
		if (pages[i]->page->getWnd() == tab) {
			pages[i]->page->closeWindow();
			pages.remove(i);
			return;
		}
	}
}

void TabHost::setTabListener(TabListener* listener_) {
	listener = listener_;
}
void TabHost::create(WndUtil parent, Rect r) {
	Super::create(parent, r);
}
HWND TabHost::insertTab(int index, String label, HBITMAP image) {
	assert(index >=0);
	boost::shared_ptr<TabPage> p(new TabPage);
	p->host = this;
	p->image = image;
	p->text = label;

	p->page.reset(new Panel);
	p->page->create(Super::getWnd(), getPageRect());
	pages.insert(index, p);
	selectTab(index);

	return p->page->getWnd();
}
HWND TabHost::appendTab(String label, HBITMAP image) {
	return insertTab(pages.length(), label, image);
}
void TabHost::selectTab(int index) {
	int sel = getSelectIndex();
	if (sel == index || index < 0) {
		pages.setTag(index);
		Super::invalidate();
		return;
	}

	if (sel >= 0 && sel < pages.size()) {
		pages[sel]->page->setVisible(false);
	}
	pages.setTag(index);
	pages[index]->page->setVisible(true);
	Super::invalidate();
}
TabPage* TabHost::getSelectTab() {
	return pages[pages.getTag()].get();
}
Rect TabHost::getTabBarRect() {
	Rect r = Super::getClientRect();
	r.bottom = tabBarHeight();
	return r;
}
Rect TabHost::getBodyRect() {
	Rect r = Super::getClientRect();
	r.top += tabBarHeight();
	return r;
}
Rect TabHost::getPageRect() {
	Rect r = getBodyRect();
	r.zoom(-pageMargin());
	return r;
}
int TabHost::getSelectIndex() {
	return pages.getTag();
}
TabPage* TabHost::getTab(int i) {
	return pages[i].get();
}
void TabHost::closeTab(int index) {
	TabPage* page = getTab(index);
	page->close();
	pages.remove(index);
	int sel = getSelectIndex();
	Log << "Old sel: " << sel << endln;
	if (sel >= pages.size()) {
		--sel;
	}
	Log << "change sel: " << sel << endl;
	selectTab(sel);
}
bool TabHost::onMouseMove(const MouseEvent& me) {
	return false;
}
bool TabHost::onMouseHover(const MouseEvent& me) {
//	Log << "Mouse Hover" << endl;
	Rect r = getTabBarRect();
	if (!r.Contains(me.x, me.y)) {
		return false;
	}
	Super::invalidate();
	Canvas g(getWnd());
	return true;
}
bool TabHost::onMouseLeave(const MouseEvent& me) {
	Log << "Mouse Leave" << endl;
	return true;
}
bool TabHost::onMouseEnter(const MouseEvent& me) {
	Log << "Mouse Enter" << endl;
	return true;
}
bool TabHost::onClick(const MouseEvent& me) {
	Rect r = getTabBarRect();
	if (r.Contains(me.x, me.y)) {
		for (int i = 0; i < pages.size(); ++i) {
			Rect rectClose = pages[i]->getCloseRect();
			if (rectClose.Contains(me.x, me.y)) {
				Log << "Close " << i << endl;
				closeTab(i);
				return true;
			}
			if (pages[i]->region.Contains(me.x, me.y)) {
				selectTab(i);
				return true;
			}
		}
	}

	return false;
}
bool TabHost::hasFocus() {
	return Super::isFocused() || Super::isChildFocused() || Super::isForegrouond() || Super::isChildForeground();
}

void TabHost::onPaint(HWND hWnd, HDC dc, PAINTSTRUCT* ps) {
	Canvas g(dc);
	//TODO onEraseBackground
	g.fillRectangle(getClientRect(), HBRUSH(COLOR_WINDOW +1));
	g.clipRect(ps->rcPaint);
	drawBody(g);
	drawBar(g);
	drawTabs(g);
}

void TabHost::drawBody(Canvas& g) {
	Rect r = getBodyRect();
	if (getSelectIndex() >= 0) {
		g.fillRectangle(r, hasFocus() ? tabFocusColor() : tabBodySelectColor());
		g.selectPenColor(tabBorderColor());
		g.moveTo(r.left, r.top);
		g.lineTo(r.left, r.bottom - 1);
		g.lineTo(r.right - 1, r.bottom - 1);
		g.lineTo(r.right - 1, r.top);
	} else {
		g.selectPenColor(tabBorderColor());
		g.selectBrushColor(tabBodyColor());
		g.rectangle(r);
	}

}
void TabHost::drawBar(Canvas& g) {
	Rect r = getTabBarRect();
	g.selectPenColor(tabBorderColor());
	g.selectBrushColor(tabBodyColor());
	g.clipRect(r);
	Rect roundRect = r;
	roundRect.bottom += diameterHeight();
	g.roundRectangle(roundRect, diameterWidth(), diameterHeight());

	g.selectPenColor(RGB(255, 0, 0) );
	g.moveTo(r.left, r.bottom);
	g.lineTo(r.right, r.bottom);

}

void TabHost::drawTabs(Canvas& g) {
	if (pages.empty()) {
		return;
	}
	int pageCount = pages.size();
	Rect barRect = getTabBarRect();
	Rgn barRgn(barRect);

//offsetX and width of every page.
	for (int i = 0, x = 0; i < pageCount; ++i) {
		x += radiusWidth();
		TabPage* page = getTab(i);
		int w = page->calTabWidth(g, x, 0, dic(dicTabBarHeight));
		x += w;
		x += radiusWidth();
	}

	//TODO calc visible tabs !!

	g.selectPenColor(tabBorderColor());
	g.selectBrushColor(tabBodyColor());
	int selectIndex = getSelectIndex();
	assert(selectIndex >=0);
//draw pre of select
	TabPage* page = 0;
	for (int i = 0; i < selectIndex; ++i) {
		calRegion(g, i, barRgn);
		page = getTab(i);
		page->drawTab(g);

	}
//draw backword of select
	for (int i = pageCount - 1; i > selectIndex; --i) {
		calRegion(g, i, barRgn);
		page = getTab(i);
		page->drawTab(g);

	}
	HBRUSH br = hasFocus() ? getFocusBrush() : getSelectBrush();
	g.selectBrush(br);
	calRegion(g, selectIndex, barRgn);
	page = getTab(selectIndex);
	page->drawTab(g);
}
void TabHost::calRegion(Canvas& g, int index, Rgn& barRgn) {
	TabPage* page = getTab(index);
	int selectIndex = getSelectIndex();

	Rect r = page->getRect();
	r.bottom += diameterHeight();
	r.left -= radiusWidth();
	r.right += radiusWidth();
	if (index < selectIndex) {
		r.right += radiusWidth();
	} else if (index > selectIndex) {
		r.left -= radiusWidth();
	}
	g.roundRectangle(r, diameterWidth(), diameterHeight());

	page->region.createRoundRectRgn(r, diameterWidth(), diameterHeight());
	page->region.AND(barRgn);

	if (index < selectIndex) {
		if (index != 0) {
			getTab(index - 1)->region.DIFF(page->region);
		}

	} else if (index > selectIndex) {
		if (index != pages.size() - 1) {
			getTab(index + 1)->region.DIFF(page->region);
		}
	} else {
		if (index != 0) {
			getTab(index - 1)->region.DIFF(page->region);
		}
		if (index != pages.size() - 1) {
			getTab(index + 1)->region.DIFF(page->region);
		}
	}
}

